package com.cpit.platform.service.impl;

import com.cpit.platform.common.constant.SystemConstant;
import com.cpit.platform.common.constant.UserConstant;
import com.cpit.platform.common.entity.Result;
import com.cpit.platform.common.entity.TableDataQueryCondition;
import com.cpit.platform.common.entity.TableJsonData;
import com.cpit.platform.common.utils.AuthUtils;
import com.cpit.platform.common.utils.BeanUtil;
import com.cpit.platform.common.utils.ResultUtil;
import com.cpit.platform.dao.*;
import com.cpit.platform.dto.*;
import com.cpit.platform.log.LogOperator;
import com.cpit.platform.service.SubSystemService;
import com.cpit.platform.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@Transactional
public class UserServiceImp implements UserService {

    private final String loggerName = this.getClass().getName();
    @Autowired
    UserRepository userRepository;
    @Autowired
    UserToRoleRepository userToRoleRepository;
    @Autowired
    SubSystemUserRepository subSystemUserRepository;
    @Autowired
    SubSystemService subSystemServ;
    @Autowired
    UserToSubSystemUserRepository userToSubSystemUserRepository;
    @Autowired
    RoleRepository roleRepository;
    @Autowired
    private OrganizationRepository orgRep = null;

    @Override
    public User findByUsername(String username) {
        LogOperator.debug(loggerName, "findByUsername: " + username);
        User u = userRepository.findByUsername(username);
        if (null == u) {
            return null;
        }
        Organization org = orgRep.findById(u.getOrganizationId());
        if (org != null) {
            u.setOrganizationName(org.getName());
        }

        List<UserToRole> urLst = userToRoleRepository.findByUserId(u.getId());
        if (urLst != null && urLst.size() > 0) {
            List<Role> roles = new ArrayList<Role>();
            for (UserToRole ur : urLst) {
                Role role = new Role();
                role.setId(ur.getRoleId());
                roles.add(role);
            }
            u.setRoles(roles);
        }

        LogOperator.debug(loggerName, "findByUsername resule: " + u);
        return u;
    }

    @Override
    public List<User> findAllByOrganizationId(int organizationId) {
        return userRepository.findAllByOrganizationId(organizationId);
    }

    @Override
    public void insert(User record) {
        userRepository.save(record);
        LogOperator.debug(loggerName, "insert, after save: " + record);
        List<Role> rs = record.getRoles();
        List<UserToRole> u2rList = new ArrayList<UserToRole>();
//		if(rs != null && rs.size() > 0) {
        for (Role r : rs) {
            UserToRole u2r = new UserToRole(r.getId(), record.getId());
            u2rList.add(u2r);
        }
        userToRoleRepository.deleteByUserId(record.getId());
        userToRoleRepository.save(u2rList);
        LogOperator.debug(loggerName, "insert, after save relation");
//		}

        List<SubSystemUser> sus = record.getSubsystemUsers();
        List<UserToSubSystemUser> u2suList = new ArrayList<UserToSubSystemUser>();
        if (sus != null && sus.size() > 0) {
            for (SubSystemUser r : sus) {
                UserToSubSystemUser u2r = new UserToSubSystemUser(record.getId(), r.getId());
                u2suList.add(u2r);
            }
            userToSubSystemUserRepository.deleteByUserId(record.getId());
            userToSubSystemUserRepository.save(u2suList);
            LogOperator.debug(loggerName, "insert, after save sub user relation");
        }
    }

    @Override
    public int deleteByPrimaryKey(Integer userid) {
        userRepository.deleteById(userid);
        userToRoleRepository.deleteByUserId(userid);
        userToSubSystemUserRepository.deleteByUserId(userid);
        return 1;
    }

    @Override
    public void updateByPrimaryKeySelective(User record) {
        insert(record);
    }

    @Override
    public void updateUserToSubsustemUser(User record) {
        LogOperator.debug(loggerName, "updateUserToSubsustemUser: " + record);
        List<SubSystemUser> sus = record.getSubsystemUsers();
        List<UserToSubSystemUser> u2suList = new ArrayList<UserToSubSystemUser>();
        if (sus != null && sus.size() > 0) {
            for (SubSystemUser r : sus) {
                UserToSubSystemUser u2r = new UserToSubSystemUser(record.getId(), r.getId());
                u2suList.add(u2r);
            }
            userToSubSystemUserRepository.deleteByUserId(record.getId());
            userToSubSystemUserRepository.save(u2suList);
            LogOperator.debug(loggerName, "insert, after save sub user relation");
        }
    }


    @Override
    public List<User> selectUserList() {
        // get user according to current login user organization code
        OrganizationPair pair;
        try {
            pair = AuthUtils.getCurrentOrganizationCode();
        } catch (Exception e) {
            LogOperator.err(loggerName, "no user login");
            return null;
        }
        String oCode = pair.organizationCode;
        List<User> rs = null;
        if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_COMMON)) {
            rs = userRepository.findByOrganizationCode(oCode);
        } else if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_AREA_ADMIN)) {
            rs = userRepository.findByOrganizationCodeStartingWith(oCode);
        } else {
            rs = userRepository.findAll();
        }
        setUserOrgName(rs);
        return rs;
    }

    @Override
    public User selectByPrimaryKey(Integer userid) {
        return userRepository.findById(userid);
    }

    @Override
    public List<User> findByCondition(int page,
                                      int size,
                                      String order,
                                      String orderBy,
                                      String orgIdCondition,
                                      String userNamePattern) {
        if (orderBy == null || orderBy.length() == 0) {
            orderBy = "id";
        }
        Direction der = order.equals(SystemConstant.DIRECTION_ASC) ? Direction.ASC : Direction.DESC;
        PageRequest request = new PageRequest(page - 1, size, new Sort(der, orderBy));
        OrganizationPair pair;
        try {
            pair = AuthUtils.getCurrentOrganizationCode();
        } catch (Exception e) {
            LogOperator.err(loggerName, "no user login", e);
            return null;
        }
        String curUserOrgCode = pair.organizationCode;
        if (orgIdCondition != null && orgIdCondition.length() > 0) {
            curUserOrgCode = orgIdCondition; // narrown down the range of data
        }
        Page<User> pr = null;
        List<User> rs = null;
        LogOperator.debug(loggerName, "findByCondition curUserOrgCode: " + curUserOrgCode);
        if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_COMMON)) {
            pr = userRepository.findByUsernameContainingAndOrganizationCode(userNamePattern, curUserOrgCode, request);
        } else if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_AREA_ADMIN)) {
            pr = userRepository.findByUsernameContainingAndOrganizationCodeStartingWith(userNamePattern, curUserOrgCode, request);
        } else {
            pr = userRepository.findByUsernameContainingAndOrganizationCodeStartingWith(userNamePattern, curUserOrgCode, request);
        }
        rs = pr.getContent();
        setUserOrgName(rs);
        return rs;
    }


    @SuppressWarnings("rawtypes")
    public TableJsonData findByConditionJqg(TableDataQueryCondition cond) {
        if (cond.getSidx() == null || cond.getSidx().length() == 0) {
            cond.setSidx("id");
        }
        Direction der = null;
        if (cond.getSord() == null || cond.getSord().length() == 0) {
            der = Direction.ASC;
        } else
            der = cond.getSord().equals(SystemConstant.DIRECTION_ASC) ? Direction.ASC : Direction.DESC;
        PageRequest request = new PageRequest(cond.getPageNumber() - 1, cond.getRowsNumber(),
                new Sort(der, cond.getSidx()));

        OrganizationPair pair;
        try {
            pair = AuthUtils.getCurrentOrganizationCode();
        } catch (Exception e) {
            LogOperator.err(loggerName, "no user login", e);
            return null;
        }
        String curUserOrgCode = pair.organizationCode;
        String orgIdCondition = BeanUtil.getStringIdentifyNull(cond.getQueryCond().get("orgIdCondition"));
        String userNamePattern = BeanUtil.getStringIdentifyNull(cond.getQueryCond().get("username"));
        if (orgIdCondition != null && orgIdCondition.length() > 0) {
            curUserOrgCode = orgIdCondition; // narrown down the range of data
        }
        Page<User> pr = null;
        LogOperator.debug(loggerName, "findByCondition curUserOrgCode: " + curUserOrgCode);
        if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_COMMON)) {
            pr = userRepository.findByUsernameContainingAndOrganizationCode(userNamePattern, curUserOrgCode, request);
        } else if (pair.userAuthType.equals(UserConstant.USER_AUTH_TYPE_AREA_ADMIN)) {
            pr = userRepository.findByUsernameContainingAndOrganizationCodeStartingWith(userNamePattern, curUserOrgCode, request);
        } else {
            pr = userRepository.findByUsernameContainingAndOrganizationCodeStartingWith(userNamePattern, curUserOrgCode, request);
        }
        List<User> rs = pr.getContent();
        setUserOrgName(rs);
        return new TableJsonData(cond, pr.getTotalElements(), rs);
    }

    public TableJsonData findSsUserByConditionJqg(TableDataQueryCondition cond) {
        if (cond.getSidx() == null || cond.getSidx().length() == 0) {
            cond.setSidx("id");
        }
        Direction der = null;
        if (cond.getSord() == null || cond.getSord().length() == 0) {
            der = Direction.ASC;
        } else
            der = cond.getSord().equals(SystemConstant.DIRECTION_ASC) ? Direction.ASC : Direction.DESC;
        PageRequest request = new PageRequest(cond.getPageNumber() - 1, cond.getRowsNumber(),
                new Sort(der, cond.getSidx()));

        Page<SubSystemUser> pr = null;
        String userNamePattern = BeanUtil.getStringIdentifyNull(cond.getQueryCond().get("username"));
        pr = subSystemUserRepository.findByUsernameContaining(userNamePattern, request);
        List<SubSystemUser> rs = pr.getContent();
        return new TableJsonData(cond, pr.getTotalElements(), rs);
    }

    @SuppressWarnings("rawtypes")
    public Result<List> queryAllSubSystemUsers() {
        List<SubSystemUser> assUser = subSystemUserRepository.findAll();
        return ResultUtil.<List>success(assUser);
    }

    @SuppressWarnings("rawtypes")
    public Result<List> queryUserRolesByUserId(Integer userid) {
        List<UserToRole> rs = userToRoleRepository.findByUserId(userid);
        List<Role> ret = new ArrayList<Role>();
        for (UserToRole r : rs) {
            Role rDto = roleRepository.findById(r.getRoleId());
            ret.add(rDto);
        }
        return ResultUtil.<List>success(ret);
    }

    @SuppressWarnings("rawtypes")
    public Result<List> querySubSystemUsersByUserId(Integer userid) {
        List<UserToSubSystemUser> rs = userToSubSystemUserRepository.findByUserId(userid);
        List<SubSystemUser> ret = new ArrayList<SubSystemUser>();
        for (UserToSubSystemUser r : rs) {
            SubSystemUser rDto = subSystemUserRepository.findById(r.getSystemUserId());
            ret.add(rDto);
        }
        return ResultUtil.<List>success(ret);
    }

    @SuppressWarnings("rawtypes")
    public Result<List> querySubSystemUsersBySystemId(Integer systemid) {
        List<SubSystemUser> rs = subSystemUserRepository.findBySubSystem(systemid);
        return ResultUtil.<List>success(rs);
    }

    @Override
    public void deleteSubSystemUserById(Integer id) {
        subSystemUserRepository.deleteById(id);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private void setUserOrgName(List<User> rs) {
        Collection<Integer> userIdList = getUserIdList(rs);
        Map<Integer, Organization> organizationMap = getOrgMap();
        Map<Integer, List<UserToRole>> utoRoleMap = getU2RoleMap(userIdList);
        Map<Integer, Role> roleMap = getRoleMap();
        Map<Integer, List<UserToSubSystemUser>> u2ssUMap = getU2SsUMap(userIdList);
        Map<Integer, SubSystemUser> ssUMap = getSsUMap();
        Map<Integer, SubSystem> ssMap = getSsMap();

        for (User u : rs) {
            Organization o = organizationMap.get(u.getOrganizationId());
            if (o != null)
                u.setOrganizationName(o.getName());
            // set roles
            List<Role> ros = new ArrayList<Role>();
            List<UserToRole> u2r = utoRoleMap.get(u.getId());
            if (u2r != null && u2r.size() > 0) {
                for (UserToRole d : u2r) {
                    Role rol = roleMap.get(d.getRoleId());
                    if (rol != null) {
                        ros.add(rol);
                    }
                }
            }
            u.setRoles(ros);

            List<SubSystemUser> ssu = new ArrayList<SubSystemUser>();
            List<UserToSubSystemUser> ssus = u2ssUMap.get(u.getId());
            if (ssus != null && ssus.size() > 0) {
                for (UserToSubSystemUser d : ssus) {
                    SubSystemUser rol = ssUMap.get(d.getSystemUserId());
                    if (rol != null) {
                        ssu.add(rol);
                    }
                }
            }
            u.setSubsystemUsers(ssu);

            SubSystem ss = ssMap.get(u.getSubSystem());
            if (ss != null) {
                u.setSubSystemName(ss.getName());
            }
        }
    }

    public Collection<Integer> getUserIdList(List<User> rs) {
        Collection<Integer> ret = new ArrayList<Integer>();
        if (rs == null || rs.size() == 0) {
            return ret;
        }
        for (User u : rs) {
            ret.add(u.getId());
        }
        return ret;
    }

    public Map<Integer, Organization> getOrgMap() {
        List<Organization> orgLst = orgRep.findAll();
        Map<Integer, Organization> ret = new HashMap<Integer, Organization>();
        for (Organization org : orgLst) {
            if (!ret.containsKey(org.getId())) {
                ret.put(org.getId(), org);
            }
        }
        return ret;
    }

    public Map<Integer, List<UserToRole>> getU2RoleMap(Collection<Integer> userIdList) {
        Map<Integer, List<UserToRole>> ret = new HashMap<Integer, List<UserToRole>>();
        List<UserToRole> lst = userToRoleRepository.findByUserIdIn(userIdList);
        for (UserToRole d : lst) {
            if (ret.containsKey(d.getUserId())) {
                List<UserToRole> ll = ret.get(d.getUserId());
                ll.add(d);
            } else {
                List<UserToRole> ll = new ArrayList<UserToRole>();
                ret.put(d.getUserId(), ll);
                ll.add(d);
            }
        }
        return ret;
    }

    public Map<Integer, Role> getRoleMap() {
        List<Role> lst = roleRepository.findAll();
        Map<Integer, Role> ret = new HashMap<Integer, Role>();
        for (Role d : lst) {
            if (!ret.containsKey(d.getId())) {
                ret.put(d.getId(), d);
            }
        }
        return ret;
    }

    public Map<Integer, SubSystemUser> getSsUMap() {
        List<SubSystemUser> lst = subSystemUserRepository.findAll();
        Map<Integer, SubSystemUser> ret = new HashMap<Integer, SubSystemUser>();
        for (SubSystemUser d : lst) {
            if (!ret.containsKey(d.getId())) {
                ret.put(d.getId(), d);
            }
        }
        return ret;
    }

    public Map<Integer, SubSystem> getSsMap() {
        List<SubSystem> lst = subSystemServ.findAll();
        Map<Integer, SubSystem> ret = new HashMap<Integer, SubSystem>();
        for (SubSystem d : lst) {
            if (!ret.containsKey(d.getId())) {
                ret.put(d.getId(), d);
            }
        }
        return ret;
    }

    public Map<Integer, List<UserToSubSystemUser>> getU2SsUMap(Collection<Integer> userIdList) {
        Map<Integer, List<UserToSubSystemUser>> ret = new HashMap<Integer, List<UserToSubSystemUser>>();
        List<UserToSubSystemUser> lst = userToSubSystemUserRepository.findByUserIdIn(userIdList);
        for (UserToSubSystemUser d : lst) {
            if (ret.containsKey(d.getUserId())) {
                List<UserToSubSystemUser> ll = ret.get(d.getUserId());
                ll.add(d);
            } else {
                List<UserToSubSystemUser> ll = new ArrayList<UserToSubSystemUser>();
                ret.put(d.getUserId(), ll);
                ll.add(d);
            }
        }
        return ret;
    }
}
